<!--
 * @Author: your name
 * @Date: 2021-03-11 16:27:50
 * @LastEditTime: 2021-03-18 10:59:11
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: \threetest\src\App2.vue
-->
<template>
  <div class="app">
    <div id="container"></div>
    <!-- <div class="config">
      <p>配置项</p>
      <h5>
        材质颜色: {{ lightColor
        }}<input
          type="color"
          :default-value="lightColor"
          v-model="lightColor"
        />
      </h5>
    </div> -->
  </div>
</template>

<script>
// import * as THREE from "@/libs/three/lib/three.js";
import * as THREE from "three/build/three.module";
import { GUI } from "three/examples/jsm/libs/dat.gui.module";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// import { DragControls } from "three/examples/jsm/controls/DragControls";
import Stats from "three/examples/jsm/libs/stats.module"; // stats的模块库
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader"; // FBXLoader库

import road from "./assets/image/1.jpg";
import oroad from "./assets/image/2.jpg";
import originalRoad from "./assets/image/4.jpg";
import selfLuminous from "./assets/image/3.jpg"; //自发光
export default {
  name: "ThreeTest",
  data() {
    return {
      camera: null,
      scene: null,
      light: null,
      renderer: null,
      mesh: null,
      mesh2: null,
      mesh3: null,
      grid: null,
      controls: null,
      group: null,
      lightColor: "yellow",
      clock: new THREE.Clock(),
      mixer: null
    };
  },
  mounted() {
    this.init();
  },
  methods: {
    init() {
      this.createScene(); // 创建场景
      this.createLight(); // 创建光源
      this.createCamera(); // 创建相机
      // this.setKeyFrame(); // 编辑关键帧
      this.loadModel(); // 加载模型
      // this.createMesh(); // 创建网格模型
      // this.createCurveMesh(); // 创建曲线模型
      // this.createLineMesh(); // 创建直线线条模型
      // this.createPlaneMesh(); // 创建平面模型
      container.addEventListener("click", this.onMouseClick, false);
      this.createRender(); // 创建渲染器
      this.createControls(); // 创建控件对象
      this.render(); // 渲染
    },
    createScene() {
      this.scene = new THREE.Scene();
      var axisHelper = new THREE.AxisHelper(500);
      this.scene.add(axisHelper);
    },
    // DirectionalLight:平行光;AmbientLight:环境(自然)光;PointLight:点光源
    createLight() {
      let ambientLight = new THREE.AmbientLight("#fff", 0.5);
      this.scene.add(ambientLight);
      let point = new THREE.DirectionalLight("#fff", 0.6);
      point.position.set(500, 500, 500);

      this.scene.add(point);

      // 设置用于计算阴影的光源对象
      point.castShadow = true;
      // 设置计算阴影的区域，最好刚好紧密包围在对象周围
      // 计算阴影的区域过大：模糊  过小：看不到或显示不完整
      point.shadow.camera.near = 0.5;
      point.shadow.camera.far = 300;
      point.shadow.camera.left = -50;
      point.shadow.camera.right = 50;
      point.shadow.camera.top = 200;
      point.shadow.camera.bottom = -100;
      point.shadow.mapSize.set(1024, 1024);
    },
    createCamera() {
      let container = document.getElementById("container");
      this.camera = new THREE.PerspectiveCamera(
        45,
        container.clientWidth / container.clientHeight,
        0.1,
        10000
      );
      // 摄像机位置
      this.camera.position.set(20, 20, 100);
    },
    loadModel() {
      const that = this;
      let loader = new FBXLoader();
      loader.load("juguangkeji.FBX", object => {
        // 海
        object.children[0].material = new THREE.MeshStandardMaterial({
          color: "#00FFFF",
          side: 2
        });
        // 地面
        object.children[1].material = new THREE.MeshStandardMaterial({
          color: "#696969",
          side: 2
        });
        // 主建筑群
        that.traverseData(object, 2, "#FF7F50");
        // 其它建筑群
        object.children[3].material = new THREE.MeshStandardMaterial({
          color: "#FFFF00",
          // map: that.loadImage(road),
          // bumpMap:  that.loadImage(oroad), // 凹凸贴图
          // specularMap:  that.loadImage(oroad), //高光贴图
          // specular: "red",
          // shininess: 3000, //高光部分的亮度，默认30
          // normalMap: that.loadImage(oroad), // 法线贴图
          side: 2
          // normalScale: new THREE.Vector2(1, 1)
          // bumpScale: 0.08 // 设置凹凸高度，默认值1。
        });
        // 树
        object.children[4].material = new THREE.MeshStandardMaterial({
          color: "green",
          side: 2
        });
        // 路
        that.traverseData(object, 5, "#A9A9A9");
        that.scene.add(object);
      });
    },
    // 遍历数据
    traverseData(object, index, color) {
      const that = this;
      if (
        object.children &&
        object.children.length > 0 &&
        object.children[index] &&
        object.children[index].children &&
        object.children[index].children.length > 0
      ) {
        object.children[index].children.map(c => {
          c.material = new THREE.MeshStandardMaterial({
            // color,
            side: 2,
            map: that.loadImage(originalRoad),
            emissive: 0xffffff, // emissive默认黑色，设置为白色
            emissiveMap: that.loadImage(selfLuminous)
          });
        });
      }
    },
    onMouseClick(event) {
      const that = this;
      let mouse = new THREE.Vector2();
      let raycaster = new THREE.Raycaster();
      event.preventDefault();
      let container = document.getElementById("container");
      let width = container.getBoundingClientRect().width;
      let height = container.getBoundingClientRect().height;
      mouse.x = (event.clientX / width) * 2 - 1;
      mouse.y = -(event.clientY / height) * 2 + 1;
      raycaster.setFromCamera(mouse, that.camera);
      let intersects = raycaster.intersectObjects(that.scene.children, true);
      if (intersects && intersects.length > 0) {
        const { name, parent, position } = intersects[0].object;
        // console.log(name);
        // console.log(parent);
        // console.log(position);
        // console.log(document.styleSheets);
        // intersects[0].object.material.color.set(that.lightColor);
        // that.outlineObj(intersects);
      }
    },
    //高亮显示模型（呼吸灯）
    // outlineObj(selectedObjects) {
    //   // 创建一个EffectComposer（效果组合器）对象，然后在该对象上添加后期处理通道。
    //   this.composer = new EffectComposer(this.renderer);
    //   // 新建一个场景通道  为了覆盖到原理来的场景上
    //   this.renderPass = new RenderPass(this.scene, this.camera);
    //   this.composer.addPass(this.renderPass);
    //   // 物体边缘发光通道
    //   this.outlinePass = new OutlinePass(
    //     new THREE.Vector2(window.innerWidth, window.innerHeight),
    //     this.scene,
    //     this.camera,
    //     selectedObjects
    //   );
    //   this.outlinePass.selectedObjects = selectedObjects;
    //   this.outlinePass.edgeStrength = 10.0; // 边框的亮度
    //   this.outlinePass.edgeGlow = 1; // 光晕[0,1]
    //   this.outlinePass.usePatternTexture = false; // 是否使用父级的材质
    //   this.outlinePass.edgeThickness = 1.0; // 边框宽度
    //   this.outlinePass.downSampleRatio = 1; // 边框弯曲度
    //   this.outlinePass.pulsePeriod = 5; // 呼吸闪烁的速度
    //   this.outlinePass.visibleEdgeColor.set(parseInt(0x00ff00)); // 呼吸显示的颜色
    //   this.outlinePass.hiddenEdgeColor = new THREE.Color(0, 0, 0); // 呼吸消失的颜色
    //   this.outlinePass.clear = true;
    //   this.composer.addPass(this.outlinePass);
    //   // 自定义的着色器通道 作为参数
    //   var effectFXAA = new ShaderPass(FXAAShader);
    //   effectFXAA.uniforms.resolution.value.set(
    //     1 / window.innerWidth,
    //     1 / window.innerHeight
    //   );
    //   effectFXAA.renderToScreen = true;
    //   this.composer.addPass(effectFXAA);
    // },
    // 加载图片(返回object)
    loadImage(imageUrl) {
      return new THREE.TextureLoader().load(imageUrl);
    },
    createMesh() {
      const that = this;
      let geometry = new THREE.BoxGeometry(20, 20, 20);
      let material = new THREE.MeshStandardMaterial({
        map: that.loadImage(originalRoad),
        side: 2
      });
      that.mesh = new THREE.Mesh(geometry, material);
      that.mesh.position.set(10, 10, 10);
      that.mesh.name = "Box";
      that.scene.add(that.mesh);

      // 设置产生投影的网格模型
      that.mesh.castShadow = true;

      //创建一个平面几何体作为投影面
      let planeGeometry = new THREE.PlaneGeometry(10, 20);
      let planeMaterial = new THREE.MeshLambertMaterial({
        color: 0x999999
      });

      // 平面网格模型作为投影面
      let planeMesh = new THREE.Mesh(planeGeometry, planeMaterial);
      that.scene.add(planeMesh); //网格模型添加到场景中
      planeMesh.rotateX(-Math.PI / 2); //旋转网格模型
      planeMesh.position.y = -10; //设置网格模型y坐标
      // 设置接收阴影的投影面
      planeMesh.receiveShadow = true;
    },
    // 编辑关键帧
    setKeyFrame() {
      let times = [0, 10];
      let values = [0, 0, 0, 500, 0, 0];
      let posTrack = new THREE.KeyframeTrack("Box.position", times, values);
      let colorKF = new THREE.KeyframeTrack(
        "Box.material.color",
        [10, 20],
        [1, 0, 0, 0, 0, 1]
      );
      let scaleTrack = new THREE.KeyframeTrack(
        "Box.scale",
        [0, 20],
        [1, 1, 1, 3, 3, 3]
      );
      let duration = 20;
      let clip = new THREE.AnimationClip("default", duration, [
        posTrack,
        colorKF,
        scaleTrack
      ]);
      this.mixer = new THREE.AnimationMixer(this.mesh);
      // 剪辑clip作为参数，通过混合器clipAction方法返回一个操作对象AnimationAction
      var AnimationAction = mixer.clipAction(clip);
      //通过操作Action设置播放方式
      AnimationAction.timeScale = 20; //默认1，可以调节播放速度
      // AnimationAction.loop = THREE.LoopOnce; //不循环播放
      AnimationAction.play(); //开始播放
    },
    // 创建曲线线条模型
    createCurveMesh() {
      const that = this;
      let geometry = new THREE.BufferGeometry();
      // 三维样条曲线  Catmull-Rom算法
      let curve = new THREE.CatmullRomCurve3([
        new THREE.Vector3(-1000, 0, 100),
        new THREE.Vector3(-500, 20, 50),
        new THREE.Vector3(0, 30, 0),
        new THREE.Vector3(500, 20, -50),
        new THREE.Vector3(1000, 0, -100)
      ]);
      // 返回一个vector3对象作为元素组成的数组
      let points = curve.getPoints(100);
      geometry.setFromPoints(points);
      let material = new THREE.LineBasicMaterial({ color: "#8FBC8F" });
      that.mesh2 = new THREE.Line(geometry, material);
      that.scene.add(that.mesh2);
    },
    // 创建直线线条模型
    createLineMesh() {
      const that = this;
      let points = [];
      points.push(new THREE.Vector3(-10, 0, 0));
      points.push(new THREE.Vector3(0, 10, 0));
      points.push(new THREE.Vector3(10, 0, 0));
      points.push(new THREE.Vector3(0, 20, 0));
      points.push(new THREE.Vector3(30, 10, 0));
      points.push(new THREE.Vector3(30, 10, 20));
      let geometry = new THREE.BufferGeometry();
      geometry.setFromPoints(points);
      let material = new THREE.LineBasicMaterial({ color: "#8FBC8F" });
      that.mesh3 = new THREE.Line(geometry, material);
      that.scene.add(that.mesh3);
    },
    // 创建平面模型
    createPlaneMesh() {
      var geometry = new THREE.PlaneGeometry(10, 20, 32);
      var material = new THREE.MeshBasicMaterial({
        color: 0xffff00,
        side: THREE.DoubleSide
      });
      var plane = new THREE.Mesh(geometry, material);
      plane.position.set(7, 7, 5);
      plane.rotateZ(Math.PI / 4);
      plane.rotateY(Math.PI / 2);
      this.scene.add(plane);
    },
    createRender() {
      let container = document.getElementById("container");
      this.renderer = new THREE.WebGLRenderer({ antialias: true });
      this.renderer.setSize(container.clientWidth, container.clientHeight);
      this.renderer.setClearColor("#fff", 1); // 设置背景颜色
      container.appendChild(this.renderer.domElement);
    },
    createControls() {
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    },
    render() {
      this.renderer.render(this.scene, this.camera);
      // this.mesh.translateX(0.01);
      // this.scene.rotation.y += 0.01;
      // this.mesh.scale.y += 0.01;
      requestAnimationFrame(this.render);
      // this.mixer.update(this.clock.getDelta());
    }
  }
};
</script>

<style lang="scss">
body {
  margin: 0;
  padding: 0;
}
.app {
  display: flex;
}
#container {
  height: 100vh;
  flex: 1;
}
.config {
  position: fixed;
  height: 100vh;
  width: 200px;
  background: #fff;
  top: 0;
  right: 0;
  padding: 5px;
}
</style>
